package net.cassite.daf4j.jpa;

import net.cassite.daf4j.*;
import net.cassite.daf4j.ds.ObjectResolver;
import net.cassite.daf4j.ds.QueryParameterParser;
import net.cassite.daf4j.util.Location;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * JPQL查询参数解释器
 */
public class JPQLQPParser implements QueryParameterParser<JPQLContext, String> {
        private ObjectResolver<JPQLContext, String> objectResolver;

        @Override
        public void top(JPQLContext jpqlContext, final int i) throws Exception {
                jpqlContext.toDoAfterJPAQueryGeneration.add(new JPQLToDo<JPQLContext>() {
                        @Override
                        public void todo(JPQLContext jpqlContext) throws Exception {
                                jpqlContext.jpaQuery.setMaxResults(i);
                        }
                });
        }

        @Override
        public void limit(JPQLContext jpqlContext, final int i, final int i1) throws Exception {
                jpqlContext.toDoAfterJPAQueryGeneration.add(new JPQLToDo<JPQLContext>() {
                        @Override
                        public void todo(JPQLContext jpqlContext) throws Exception {
                                jpqlContext.jpaQuery.setFirstResult(i).setMaxResults(i1 - i);
                        }
                });
        }

        @Override
        public void orderBy(JPQLContext jpqlContext, OrderBase[] orderBases) throws Exception {
                jpqlContext.generalJPQL.append(" ORDER BY ");
                boolean isFirst = true;
                for (OrderBase order : orderBases) {
                        if (isFirst) {
                                isFirst = false;
                        } else {
                                jpqlContext.generalJPQL.append(", ");
                        }
                        jpqlContext.generalJPQL
                                .append(objectResolver.resolve(jpqlContext, order.data))
                                .append(" ")
                                .append(order.type == OrderTypes.asc ? "ASC" : "DESC");
                }
        }

        @Override
        public void focus(JPQLContext jpqlContext, Map<Selectable, String> map) throws Exception {
                jpqlContext.frontQueries = new StringBuilder();
                StringBuilder sb = jpqlContext.frontQueries;
                sb.append("SELECT ");

                // generate map
                if (map == null) {
                        map = new LinkedHashMap<Selectable, String>();
                }
                if (map.size() == 0) {
                        for (Field f : jpqlContext.entity.getClass().getFields()) {
                                if (IData.class.isAssignableFrom(f.getType()) && !ParameterAggregate.class.isAssignableFrom(f.getType())) {
                                        map.put((IData<?>) f.get(jpqlContext.entity), jpqlContext.entityClass.getSimpleName() + "." + f.getName());
                                }
                        }
                }

                boolean NotDistinctOrAlreadyDone = jpqlContext.NoDistinctOrAlreadyDone;
                boolean isFirst = true;
                for (Selectable o : map.keySet()) {
                        if (isFirst) {
                                isFirst = false;
                        } else {
                                sb.append(", ");
                        }
                        if (o instanceof IData && !(o instanceof ParameterAggregate)) {
                                jpqlContext.selectNonAggregationAliases.add(jpqlContext.aliasMap.get(JPQLEntityDataParser.findEntity(((IData) o).getEntity(), jpqlContext)) + "." + DataUtils.findFieldNameByIData((IData<?>) o));
                                if (!NotDistinctOrAlreadyDone) {
                                        NotDistinctOrAlreadyDone = true;
                                        sb.append("DISTINCT ");
                                }
                                sb.append(objectResolver.resolve(jpqlContext, o));
                        } else if (o instanceof IExpression && DataUtils.expressionIsAggregate((IExpression) o)) {
                                jpqlContext.RequireGroupBy = true;
                                if (!NotDistinctOrAlreadyDone) {
                                        NotDistinctOrAlreadyDone = true;
                                        String str = objectResolver.resolve(jpqlContext, o);
                                        sb.append(str.substring(0, str.indexOf("(") + 1) + "DISTINCT " + str.substring(str.indexOf("(") + 1));
                                } else {
                                        sb.append(objectResolver.resolve(jpqlContext, o));
                                }
                        }
                }
        }

        @Override
        public void distinct(JPQLContext jpqlContext) throws Exception {
                // DO DISTINCT AT SELECT GENERATING STEP
        }

        @Override
        public void setObjectResolver(ObjectResolver<JPQLContext, String> objectResolver) {
                this.objectResolver = objectResolver;
        }
}
